/*  

Cafechess Library: Cover basic chess programming needs, like GUI, Engine integration, 
FEN and PGN parsing.

                 \\|//
                 (o o)
--------------ooO-(_)-Ooo----------------------------------
Copyright (C) 2009  By Gregory A. Prentice Snoqualmie, WA
                    www.cafechess.org
                    cafechess@comcast.net
                      Oooo
--------------oooO----(  )---------------------------------
              (  )    (_/
               \_)

Microsoft Public License (Ms-PL)
               
Revisions:

Author        Date    Description:

*/
using System;
using Cafechess.Chess.General;
namespace Cafechess.Chess.Validation
{
  using Bitboard = System.UInt64;
  /// <summary>
  /// Defines the bitboard positions and special move availability for one color.
  /// Normally used in pairs, one for white and black.
  /// </summary>
  //[CLSCompliant(false)]
  public class ChessPosition
  {
    public Bitboard Board;
    public Bitboard King;
    public Bitboard Queens;
    public Bitboard Rooks;
    public Bitboard Bishops;
    public Bitboard Knights;
    public Bitboard Pawns;
    public Bitboard RotatedR90;
    public Bitboard RotatedL90;
    public Bitboard RotatedA1H8;
    public Bitboard RotatedA8H1;
    public Bitboard Pinning;
    public Bitboard Pinned;
    /// <summary>
    /// Used to show the the opponent may capture
    /// our pawn by enpassant on his move.
    /// </summary>
    public Bitboard Enpassant;
    /// <summary>
    /// Used to show that we can capture the opponents
    /// pawn enpassant this move.
    /// </summary>
    public Bitboard CaptureEnpassant;
    public bool canCastleKingside;
    public bool canCastleQueenside;
    public bool Color;
    public bool IsKingMated;
    public bool IsKingInCheck;
    public bool IsKingStaleMated;
    /// <summary>
    /// Standard constructor that requires you to specify the color the position represents.
    /// </summary>
    /// <param name="bColor"></param>
    public ChessPosition(bool bColor)
    {
      Color = bColor;
      NewBoard();
    }
    /// <summary>
    /// A copy constructor used for cloning a position.
    /// </summary>
    /// <param name="position"></param>
    public ChessPosition(ChessPosition position)
    {
      Copy(position);
    }
    /// <summary>
    /// Used to set one position equal to another.
    /// </summary>
    /// <param name="position"></param>
    public void Copy(ChessPosition position)
    {
      Board=position.Board;
      King=position.King;
      Queens=position.Queens;
      Rooks=position.Rooks;
      Bishops=position.Bishops;
      Knights=position.Knights;
      Pawns=position.Pawns;
      RotatedR90=position.RotatedR90;
      RotatedL90=position.RotatedL90;
      RotatedA1H8=position.RotatedA1H8;
      RotatedA8H1=position.RotatedA8H1;
      Pinning=position.Pinning;
      Pinned=position.Pinned;
      Enpassant=position.Enpassant;
      CaptureEnpassant = position.CaptureEnpassant;
      canCastleKingside=position.canCastleKingside;
      canCastleQueenside=position.canCastleQueenside;      
      Color=position.Color;
      IsKingInCheck=position.IsKingInCheck;
      IsKingMated=position.IsKingMated;
      IsKingStaleMated=position.IsKingStaleMated;
    }

    /// <summary>
    /// Completely clears a position to an empty state.
    /// </summary>
    public void ClearBoard()
    {
      Board=0;
      King=0;
      Queens=0;
      Rooks=0;
      Bishops=0;
      Knights=0;
      Pawns=0;
      RotatedR90=0;
      RotatedL90=0;
      RotatedA1H8=0;
      RotatedA8H1=0;
      Pinning=0;
      Pinned=0;
      Enpassant=0;
      CaptureEnpassant=0;
      canCastleKingside=true;
      canCastleQueenside=true;
      IsKingMated=false;
      IsKingInCheck=false;
      IsKingStaleMated=false;

    }
    /// <summary>
    /// Sets up the default bitboards based on the color of the pieces.
    /// </summary>
    public void NewBoard()
    {
      if( Color )
      {
        // White starting bitboards.
        Pawns=(Bitboard)0x000000000000FF00;
        Rooks=(Bitboard)0x0000000000000081;
        Knights=(Bitboard)0x0000000000000042;
        Bishops=(Bitboard)0x0000000000000024;
        Queens=(Bitboard)0x0000000000000010;
        King=(Bitboard) 0x0000000000000008;
        Board=(Bitboard)0x000000000000FFFF;
        RotatedR90=(Bitboard)0xC0C0C0C0C0C0C0C0;
        // Not correct.
        RotatedL90=(Bitboard)0xC0C0C0C0C0C0C0C0;;
        RotatedA1H8=(Bitboard)0x6030180C060381C0;
        RotatedA8H1=(Bitboard)0x03060C183060C081;
      }
      else
      {
        // Blacks starting bitboards.
        Pawns=(Bitboard)0x00FF000000000000;
        Rooks=(Bitboard)0x8100000000000000;
        Knights=(Bitboard)0x4200000000000000;
        Bishops=(Bitboard)0x2400000000000000;
        Queens=(Bitboard)0x1000000000000000;
        King=(Bitboard) 0x0800000000000000;
        Board=(Bitboard)0xFFFF000000000000;
        RotatedR90=(Bitboard)0x0303030303030303;
        // Not correct
        RotatedL90=(Bitboard)0x0303030303030303;
        RotatedA1H8=(Bitboard)0x81C06030180C0603;
        RotatedA8H1=(Bitboard)0xC08103060C183060;
      }
      Pinning=0x00;
      Pinned=0x00;
      Enpassant=0x00;
      CaptureEnpassant = 0x00;
      canCastleKingside=true;
      canCastleQueenside=true;
      IsKingMated=false;
      IsKingInCheck=false;
      IsKingStaleMated=false;
    }

    /// <summary>
    /// Adds the requested piece into the proper bitboards.
    /// </summary>
    /// <param name="piece"></param>
    /// <param name="mask"></param>
    public void AddPiece(Pieces piece, Bitboard mask)
    {
      switch(piece)
      {
        case Pieces.WhiteKing:
        case Pieces.BlackKing:
          this.King |= mask;
          break;
        case Pieces.WhiteQueen:
        case Pieces.BlackQueen:
          this.Queens |= mask;
          break;
        case Pieces.WhiteRook:
        case Pieces.BlackRook:
          this.Rooks |= mask;
          break;
        case Pieces.WhiteBishop:
        case Pieces.BlackBishop:
          this.Bishops |= mask;
          break;
        case Pieces.WhiteKnight:
        case Pieces.BlackKnight:
          this.Knights |= mask;
          break;
        case Pieces.WhitePawn:
        case Pieces.BlackPawn:
          this.Pawns |= mask;
          break;
      }
    }

    /// <summary>
    /// Removes the requested piece from the bitboards.
    /// </summary>
    /// <param name="piece"></param>
    /// <param name="mask"></param>
    public void RemovePiece(Pieces piece, Bitboard mask)
    {
      switch(piece)
      {
        case Pieces.WhiteKing:
        case Pieces.BlackKing:
          this.King &= ~mask;
          break;
        case Pieces.WhiteQueen:
        case Pieces.BlackQueen:
          this.Queens &= ~mask;
          break;
        case Pieces.WhiteRook:
        case Pieces.BlackRook:
          this.Rooks &= ~mask;
          break;
        case Pieces.WhiteBishop:
        case Pieces.BlackBishop:
          this.Bishops &= ~mask;
          break;
        case Pieces.WhiteKnight:
        case Pieces.BlackKnight:
          this.Knights &= ~mask;
          break;
        case Pieces.WhitePawn:
        case Pieces.BlackPawn:
          this.Pawns &= ~mask;
          break;
      }
    }


    /// <summary>
    /// Moves the the given piece in the bitboards.  May want to consider moving all of
    /// the movePiece logic into the ChessPosition.removePiece function.  This routine
    /// basically mimics the ChessBitboard.removePiece and ChessBitboard.addPiece so maybe
    /// simply call those routines to to simplify the logic.  Currenty only being done
    /// for a small optimization gain.
    /// </summary>
    /// <param name="piece">Piece we are deleting, note that the ChessPostion
    /// class ignores the color of the piece as basically it is color blind.</param>
    /// <param name="square"></param>
    /// <param name="Pieces">Current bitboards to update.</param>
    public void MovePiece(Pieces piece, BitboardSquare from, BitboardSquare to)
    {
      // Move the piece.
      RemovePiece(piece,from.SquareMask());
      AddPiece(piece,to.SquareMask());
      // Remove any prevoious Enpassant.
      Enpassant = 0x00;
      // See if this move puts a pawn in danger of Enpassant      
      if( piece == Pieces.BlackPawn || piece == Pieces.WhitePawn )
      {
        if( IsPotentialEnpassant(from.SquareMask(), to.SquareMask()) )
          // Set square of pawn susceptible to enpasant attack.
          Enpassant = to.SquareMask();
      }
      Board = (Board & (~from.SquareMask()))|to.SquareMask();
      // Update attacker rotated boards:
      Bitboard maskFrom = from.A8H1Mask();// getBitSquare(coFrom.A8H1Square);
      Bitboard maskTo   = to.A8H1Mask();//getBitSquare(coTo.A8H1Square);
      RotatedA8H1 = (RotatedA8H1 & (~maskFrom)) | maskTo;
      maskFrom = from.A1H8Mask();//getBitSquare(coFrom.A1H8Square);
      maskTo   = to.A1H8Mask();//getBitSquare(coTo.A1H8Square);
      RotatedA1H8 = (RotatedA1H8& (~maskFrom)) | maskTo;
      maskFrom = from.R90Mask();//getBitSquare(coFrom.R90Square);
      maskTo   = to.R90Mask();//getBitSquare(coTo.R90Square);
      RotatedR90 = (RotatedR90 & (~maskFrom)) | maskTo;
    }

    /// <summary>
    /// Determine if the move may allow our opponent to capture our
    /// pawn with enpassant his next move.
    /// </summary>
    /// <param name="from"></param>
    /// <param name="to"></param>
    /// <returns></returns>
    public bool IsPotentialEnpassant(Bitboard from, Bitboard to)
    {
      bool bOk = false;
      // See if a black Enpassant move.
      if( ((0x00FF000000000000 & from) != 0 ) &&
        ((0x000000FF00000000 & to) !=0 ) )
        bOk = true;
      // See if a white Enpassant move.
      else if( ((0x000000000000FF00 & from) != 0 ) &&
        ((0x00000000FF000000 & to) !=0 ) )
        bOk = true;
      return bOk;
    }

    /// <summary>
    /// Adds the given piece to the bitboards.  May want to consider moving all of
    /// the addPiece logic into the ChessPosition.removePiece function.
    /// </summary>
    /// <param name="piece">Piece we are deleting, note that the ChessPostion
    /// class ignores the color of the piece as basically it is color blind.</param>
    /// <param name="Pieces">Current bitboards to update.</param>
    public void AddPiece(Pieces piece, BitboardSquare to)
    {
      // Add the piece to the piece board.
      AddPiece(piece, to.SquareMask());
      // Update main board.
      Board  = Board |to.SquareMask();
      // Update rotated boards:
      Bitboard maskTo   = to.A8H1Mask(); // getBitSquare(coTo.A8H1Square);
      RotatedA8H1 = RotatedA8H1 | maskTo;
      maskTo   = to.A1H8Mask(); // getBitSquare(coTo.A1H8Square);
      RotatedA1H8 = RotatedA1H8 | maskTo;
      maskTo   = to.R90Mask(); // getBitSquare(coTo.R90Square);
      RotatedR90 = RotatedR90 | maskTo;
    }

    /// <summary>
    /// Removes the given piece from the bitboards.  May want to consider moving all of
    /// the removePiece logic into the ChessPosition.removePiece function.
    /// </summary>
    /// <param name="piece">Piece we are deleting, note that the ChessPostion
    /// class ignores the color of the piece as basically it is color blind.</param>
    /// <param name="square"></param>
    /// <param name="Pieces"></param>
    public void RemovePiece(Pieces piece, BitboardSquare square )
    {
      // Get the squares.
      Bitboard mask = square.SquareMask(); 
      RemovePiece(piece,mask);
      Board = Board & ~mask;
      // Update rotated boards:
      mask   = square.A8H1Mask(); 
      RotatedA8H1 = RotatedA8H1 & ~mask;
      mask   = square.A1H8Mask(); 
      RotatedA1H8 = RotatedA1H8 & ~mask;
      mask   = square.R90Mask();
      RotatedR90 = RotatedR90 & ~ mask;
    }

    /// <summary>
    /// Removes the piece from the bitboards.  Figures out which piece needs to be removed.
    /// </summary>
    /// <param name="square">The square which must be check for the presence of a piece.</param>
    public Pieces RemovePiece(BitboardSquare square)
    {
      Pieces cp = Pieces.None;
      Bitboard mask = square.SquareMask(); // getBitSquare(bbSquare.Square);
      if( (mask & Pawns) != 0 )
        cp = this.Color ? Pieces.WhitePawn : Pieces.BlackPawn;
      else if( (mask & Knights) != 0 )
        cp = this.Color ? Pieces.WhiteKnight : Pieces.BlackKnight;
      else if( (mask & Bishops) != 0 )
        cp = this.Color ? Pieces.WhiteBishop : Pieces.BlackBishop;
      else if( (mask & Rooks) != 0 )
        cp = this.Color ? Pieces.WhiteRook : Pieces.BlackRook;
      else if( (mask & Queens) != 0 )
        cp = this.Color ? Pieces.WhiteQueen : Pieces.BlackQueen;
      else if( (mask & King) != 0 )
        cp = this.Color ? Pieces.WhiteKing : Pieces.BlackKing;
      if( cp != Pieces.None )
        RemovePiece(cp,square);
      return cp;
    }

    /// <summary>
    /// Returns the requested piece bitboard.  Note you may have multiple bits set for all but
    /// the kings bitboard.
    /// </summary>
    /// <param name="piece"></param>
    /// <returns></returns>
    public Bitboard GetPiece(Pieces piece)
    {
      Bitboard mask = 0;
      switch(piece)
      {
        case Pieces.WhiteKing:
        case Pieces.BlackKing:
          mask = this.King;
          break;
        case Pieces.WhiteQueen:
        case Pieces.BlackQueen:
          mask = this.Queens;
          break;
        case Pieces.WhiteRook:
        case Pieces.BlackRook:
          mask = this.Rooks;
          break;
        case Pieces.WhiteBishop:
        case Pieces.BlackBishop:
          mask = this.Bishops;
          break;
        case Pieces.WhiteKnight:
        case Pieces.BlackKnight:
          mask = this.Knights;
          break;
        case Pieces.WhitePawn:
        case Pieces.BlackPawn:
          mask = this.Pawns;
          break;
      }
      return mask;
    }
  }
}
